package top.hmtools.security;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 非对称 RSA 加密算法工具
 * 
 * @author HyboJ 创建日期：2017-1-13上午11:00:04
 */
public class RSATools {
	protected static Logger logger = LoggerFactory.getLogger(RSATools.class);

	/**
	 * 加密解密方式
	 */
	public static final String KEY_ALGORITHM = "RSA";

	/**
	 * 貌似默认是RSA/NONE/PKCS1Padding，未验证
	 */
	public static final String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding";

	/**
	 * 公钥键名称
	 */
	public static final String PUBLIC_KEY = "publicKey";
	/**
	 * 私钥键名称
	 */
	public static final String PRIVATE_KEY = "privateKey";

	/**
	 * RSA密钥长度必须是64的倍数，在512~65536之间。默认是1024
	 */
	public static final int KEY_SIZE = 2048;
	
	/**
	 * 解密
	* 方法说明：                    RSADecode
	* 输入参数说明：           @param privateKeyStr   base64 编码后的字符串（RSA私钥）
	* 输入参数说明：           @param encodedText
	* 输入参数说明：           @return
	* 输出参数说明：           byte[]
	*
	*
	 */
	public static byte[] RSADecode(String privateKeyStr, byte[] encodedText) {
	    byte[] decodeBASE64PK = decodeBASE64(privateKeyStr);
	    return RSADecode(decodeBASE64PK,encodedText);
	}

	/**
	 * 解密
	 * 
	 * @param privateKeyByte
	 * @param encodedText
	 * @return
	 */
	public static byte[] RSADecode(byte[] privateKeyByte, byte[] encodedText) {
		PrivateKey restorePrivateKey = RSATools.restorePrivateKey(privateKeyByte);
		return RSADecode(restorePrivateKey, encodedText);
	}

	/**
	 * 解密
	 * 
	 * @param key
	 * @param encodedText
	 * @return
	 */
	public static byte[] RSADecode(PrivateKey key, byte[] encodedText) {
		byte[] result = null;
		try {
			Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
			cipher.init(Cipher.DECRYPT_MODE, key);
			result = cipher.doFinal(encodedText);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}
	
	/**
	 * 加密
	 * @param publicKeyStr		base64 编码后的字符串（RSA公钥）
	 * @param plainText
	 * @return
	 */
	public static byte[] RSAEncode(String publicKeyStr,byte[] plainText){
		byte[] decodeBASE64PK = decodeBASE64(publicKeyStr);
		return RSAEncode(decodeBASE64PK,plainText);
	}
	
	/**
	 * 加密
	* 方法说明：                    RSAEncode
	* 输入参数说明：           @param publicKeyByte
	* 输入参数说明：           @param plainText
	* 输入参数说明：           @return
	* 输出参数说明：           byte[]
	*
	*
	 */
	public static byte[] RSAEncode(byte[] publicKeyByte,byte[] plainText){
	    PublicKey restorePublicKey = restorePublicKey(publicKeyByte);
        byte[] rsaEncode = RSAEncode(restorePublicKey,plainText);
        return rsaEncode;
	}

	/**
	 * 加密
	 * 
	 * @param key
	 * @param plainText
	 * @return
	 */
	public static byte[] RSAEncode(PublicKey key, byte[] plainText) {
		byte[] result = null;
		try {
			Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
			cipher.init(Cipher.ENCRYPT_MODE, key);
			result = cipher.doFinal(plainText);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}
	
	/**
	 * 还原私钥，PKCS8EncodedKeySpec 用于构建私钥的规范
	* 方法说明：                    restorePrivateKey
	* 输入参数说明：           @param keyBase64Str    base64编码的私钥字符串
	* 输入参数说明：           @return
	* 输出参数说明：           PrivateKey
	*
	*
	 */
	public static PrivateKey restorePrivateKey(String keyBase64Str) {
	    byte[] decodeBASE64 = decodeBASE64(keyBase64Str);
	    return restorePrivateKey(decodeBASE64);
	}

	/**
	 * 还原私钥，PKCS8EncodedKeySpec 用于构建私钥的规范
	 * 
	 * @param keyBytes
	 * @return
	 */
	public static PrivateKey restorePrivateKey(byte[] keyBytes) {
		PrivateKey result = null;
		try {
			PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(	keyBytes);
			KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
			result = factory.generatePrivate(pkcs8EncodedKeySpec);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (InvalidKeySpecException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	/**
	 * 还原公钥，X509EncodedKeySpec 用于构建公钥的规范
	* 方法说明：                    restorePublicKey
	* 输入参数说明：           @param keyBase64Str    base64编码后的公钥字符串
	* 输入参数说明：           @return
	* 输出参数说明：           PublicKey
	*
	*
	 */
	public static PublicKey restorePublicKey(String keyBase64Str) {
	    byte[] decodeBASE64 = decodeBASE64(keyBase64Str);
	    return restorePublicKey(decodeBASE64);
	}

	/**
	 * 还原公钥，X509EncodedKeySpec 用于构建公钥的规范
	 * 
	 * @param keyBytes
	 * @return
	 */
	public static PublicKey restorePublicKey(byte[] keyBytes) {
		PublicKey result = null;

		try {
			// X509EncodedKeySpec 用于构建公钥的规范
			X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(
					keyBytes);
			KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
			result = factory.generatePublic(x509EncodedKeySpec);
		} catch (InvalidKeySpecException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	/**
	 * 获取RSA公钥私钥对（base64编码后）
	 * @param keySize  RSA密钥长度必须是64的倍数，在512~65536之间。默认是 2048
	 * @return
	 */
	public static Map<String,String> getGenerateKeyBase64Str(Integer keySize){
		Map<String, String> result = new HashMap<String, String>();
		Map<String, byte[]> generateKeyBytes = getGenerateKeyBytes(keySize);
		result.put(PRIVATE_KEY, encodeBASE64(generateKeyBytes.get(PRIVATE_KEY)));
		result.put(PUBLIC_KEY, encodeBASE64(generateKeyBytes.get(PUBLIC_KEY)));
		return result;
	}

	/**
	 * 获取RSA公钥私钥对
	 * 
	 * @param keySize
	 *            RSA密钥长度必须是64的倍数，在512~65536之间。默认是 2048
	 * @return
	 */
	public static Map<String, byte[]> getGenerateKeyBytes(Integer keySize) {
		Map<String, byte[]> result = new HashMap<String, byte[]>();
		try {
			// 获取生成公钥私钥对 的 对象实例
			KeyPairGenerator keyPairGenerator = KeyPairGenerator
					.getInstance(KEY_ALGORITHM);
			// 初始化密钥对生成器
			if (keySize != null && keySize < 65536 && keySize > 512
					&& keySize % 64 == 0) {
				keyPairGenerator.initialize(keySize);
			} else {
				keyPairGenerator.initialize(KEY_SIZE);
			}

			KeyPair keyPair = keyPairGenerator.generateKeyPair();
			RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
			RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

			result.put(PUBLIC_KEY, publicKey.getEncoded());
			result.put(PRIVATE_KEY, privateKey.getEncoded());
			logger.debug("########################################################");
			logger.debug(PUBLIC_KEY + "：" + publicKey.getEncoded());
			logger.debug(PRIVATE_KEY + "：" + privateKey.getEncoded());
			logger.debug("########################################################");
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * base64编码
	 *<br> top.hmtools.security.BASE64.encode(byte[])
	 * @param bstr
	 * @return String
	 */
	public static String encodeBASE64(byte[] bstr) {
		return Base64Tools.encode(bstr);
	}

	/**
	 * base64解码
	 * <br>top.hmtools.security.BASE64.decode(String)
	 * @param str
	 * @return string
	 */
	public static byte[] decodeBASE64(String str) {
		return Base64Tools.decode(str);
	}

}
